package pers.myutil.local.conversion.data;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import pers.myutil.throwable.local.MyUtilLocalException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.*;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.StringWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * Map 转换
 */
public class MapConversion {

    /**
     * 参数转换MAP 转 String
     * @param param Map 参数
     * @return String
     */
    public static String mapToString(Map<String, Object> param){
        StringBuilder stringBuilder=new StringBuilder();
        if (param != null && param.size() > 0) {
            for (Map.Entry<String, Object> er : param.entrySet()) {
                stringBuilder.append(er.getKey());
                stringBuilder.append("=");
                stringBuilder.append(er.getValue());
                stringBuilder.append("&");
            }
        }
        stringBuilder.deleteCharAt(stringBuilder.length() - 1);
        return stringBuilder.toString();
    }

    /**
     * 实体转Map
     * @param object 对象
     * @return map
     * @throws MyUtilLocalException 异常
     * @throws IllegalAccessException 安全权限异常
     */
    public static Map<String,Object> objectToMap(Object object) throws MyUtilLocalException, IllegalAccessException {
        if(object == null){
            throw new MyUtilLocalException("对象不能为空！");
        }
        Map<String,Object> map = new HashMap<String, Object>();
        Class clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields){
            field.setAccessible(true);
            map.put(field.getName(),field.get(object));
        }
        return map;
    }

    /**
     * Map转为对象
     * @param param map参数
     * @param clazz 实体类
     * @return
     * @throws NoSuchMethodException
     * @throws IntrospectionException
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws MyUtilLocalException
     * @throws InvocationTargetException
     */
    public static <T> T mapToObject(Map<String,Object> param,Class<T> clazz) throws NoSuchMethodException, IntrospectionException, IllegalAccessException, InstantiationException, MyUtilLocalException, InvocationTargetException {
        return mapToObject(param,clazz,null);
    }

    /**
     * Map转对象才注解
     * @param param map参数
     * @param clazz 实体类
     * @param annotationClass 注解类
     * @return
     * @throws MyUtilLocalException
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws InstantiationException
     * @throws IntrospectionException
     */
    public static <T> T mapToObject(Map<String,Object> param,Class<T> clazz,Class<? extends Annotation> annotationClass) throws MyUtilLocalException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IntrospectionException {
        if(param == null || param.size() <= 0){
            throw new MyUtilLocalException("param不能为空或者param值不能为空！");
        }
        if(clazz == null){
            throw new MyUtilLocalException("实体类不能为空！");
        }
        // 获取对象
        T object = clazz.getDeclaredConstructor().newInstance();
        // 获取属性列表
      for (Field field : object.getClass().getDeclaredFields()){
        // 创建属性描述
          PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(),object.getClass());
          // 将属性首字母转换为大写并获取名称
          String propertyName =  propertyDescriptor.getName().substring(0, 1).toUpperCase() + propertyDescriptor.getName().substring(1);
          // 获取修饰符 public private
          int mod = field.getModifiers();
          // 判断属性是否为静态或者是常量则跳出循环进行下一循环
          if(Modifier.isStatic(mod) || Modifier.isFinal(mod)){
              continue;
          }
          Method method = null;
          try {
              method = clazz.getMethod("get"+propertyName);
          } catch (NoSuchMethodException ignored) {}
          try {
              method = clazz.getMethod("is"+propertyName);
          } catch (NoSuchMethodException ignored){}
          if(method == null){
              continue;
          }
          if(annotationClass != null) {
              if (method.isAnnotationPresent(annotationClass) && field.isAnnotationPresent(annotationClass)) {
                  throw new MyUtilLocalException(clazz.getName() + ".$" + field.getName() + "：方法注解与属性注解不能同时存在！");
              } else
                  // 判断方法是否存存在注解
                  if (method.isAnnotationPresent(annotationClass)) {
                      // 获取方法注解
                      Annotation annotation = method.getAnnotation(annotationClass);
                      setObjectValueByAnnotation(annotation, propertyDescriptor, object, param);
                  } else if (field.isAnnotationPresent(annotationClass)) {
                      // 获取属性注解
                      Annotation annotation = field.getAnnotation(annotationClass);
                      setObjectValueByAnnotation(annotation, propertyDescriptor, object, param);
                  } else {
                      setObjectValue(param, object, propertyDescriptor, propertyName);
                  }
          }else {
              setObjectValue(param, object, propertyDescriptor, propertyName);
          }
      }
      return object;
  }

    /**
     * 设置对象值
     * @param param map参数
     * @param object 对象
     * @param propertyDescriptor 对象属性
     * @param propertyName 属性名称
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    static <T> void setObjectValue(Map<String, Object> param, T object, PropertyDescriptor propertyDescriptor, String propertyName) throws IllegalAccessException, InvocationTargetException {
        if (param.containsKey(propertyDescriptor.getName())) {
            propertyDescriptor.getWriteMethod().invoke(object, getMapValue(param, propertyDescriptor.getName()));
        } else if (param.containsKey(propertyName)) {
            propertyDescriptor.getWriteMethod().invoke(object, getMapValue(param, propertyName));
        }
    }

    /**
     * 根据注解设置对象
     * @param annotation 注解对象
     * @param propertyDescriptor 属性
     * @param object 对象
     * @param param map参数
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    static <T> void setObjectValueByAnnotation(Annotation annotation,PropertyDescriptor propertyDescriptor,T object,Map<String,Object> param)throws IllegalAccessException, InvocationTargetException {
      // 遍历注解方法
      for (Method annotationMethod : annotation.getClass().getDeclaredMethods()){
          String annotationMethodName = null;
          try {
              annotationMethodName = (String) annotationMethod.invoke(annotation, null);
          }catch (IllegalArgumentException ignored){
          }catch (ClassCastException ignored){}
          if(annotationMethod.getName().equals("toString")){
              continue;
          }
          if(annotationMethodName == null || annotationMethodName.equals("")){
              // 获取map的值
              try {
                  propertyDescriptor.getWriteMethod().invoke(object,getMapValue(param,propertyDescriptor.getName()));
              }catch (IllegalArgumentException ignored){}
          }else {
              // 获取map的值
              propertyDescriptor.getWriteMethod().invoke(object,getMapValue(param,annotationMethodName));
          }
      }
  }


    /**
     * 获取mao值
     * @param map map参数
     * @param propertyDescriptorName 对象属性
     * @return
     */
    static Object[] getMapValue(Map<String, Object> map, String propertyDescriptorName)  {
        Object value = map.get(propertyDescriptorName);
        Object[] args = new Object[1];
        args[0] = value;
        return args;
    }

    /**
     * MAP转XML
     * @param param map参数
     * @return XML字符串
     * @throws ParserConfigurationException
     * @throws TransformerException
     * @throws MyUtilLocalException
     */
    public static String mapToXml(Map<String,Object> param) throws ParserConfigurationException, TransformerException, MyUtilLocalException {
        if(param == null){
            throw new MyUtilLocalException("map参数不能为空！");
        }
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder= documentBuilderFactory.newDocumentBuilder();
        org.w3c.dom.Document document = documentBuilder.newDocument();
        org.w3c.dom.Element root = document.createElement("xml");
        document.appendChild(root);
        for (String key: param.keySet()) {
            String value = String.valueOf(param.get(key));
            if (value == null) {
                value = "";
            }
            value = value.trim();
            org.w3c.dom.Element filed = document.createElement(key);
            filed.appendChild(document.createTextNode(value));
            root.appendChild(filed);
        }
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        DOMSource source = new DOMSource(document);
        transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");
        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        transformer.transform(source, result);
        String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");
        try {
            writer.close();
        }
        catch (Exception ex) {
        }
        return output;
    }

    /**
     * XML字符串转Map
     * @param xml xml字符串
     * @return Map对象
     */
    public static Map<String,Object> xmlToMap(String xml) throws DocumentException {
        Document doc = DocumentHelper.parseText(xml);
        Map<String, Object> map = new HashMap<String, Object>();
        if(doc == null)
            return map;
        Element root = doc.getRootElement();
        for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {
            Element e = (Element) iterator.next();
            List list = e.elements();
            if(list.size() > 0){
                map.put(e.getName(), Element2Map(e));
            }else
                map.put(e.getName(), e.getText());
        }
        return map;
    }

    /**
     * xml元素转Map
     * @param e xml元素
     * @return MAP
     */
    public static Map<String,Object> Element2Map(Element e){
        Map<String,Object> map = new HashMap<String,Object>();
        List list = e.elements();
        if(list.size() > 0){
            for (Object aList : list) {
                Element iter = (Element) aList;
                List mapList = new ArrayList();

                if (iter.elements().size() > 0) {
                    Map<String, Object> m = Element2Map(iter);
                    if (map.get(iter.getName()) != null) {
                        Object obj = map.get(iter.getName());
                        if (!obj.getClass().getName().equals("java.util.ArrayList")) {
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(m);
                        }
                        if (obj.getClass().getName().equals("java.util.ArrayList")) {
                            mapList = (List) obj;
                            mapList.add(m);
                        }
                        map.put(iter.getName(), mapList);
                    } else
                        map.put(iter.getName(), m);
                } else {
                    if (map.get(iter.getName()) != null) {
                        Object obj = map.get(iter.getName());
                        if (!obj.getClass().getName().equals("java.util.ArrayList")) {
                            mapList = new ArrayList();
                            mapList.add(obj);
                            mapList.add(iter.getText());
                        }
                        if (obj.getClass().getName().equals("java.util.ArrayList")) {
                            mapList = (List) obj;
                            mapList.add(iter.getText());
                        }
                        map.put(iter.getName(), mapList);
                    } else
                        map.put(iter.getName(), iter.getText());
                }
            }
        }else
            map.put(e.getName(), e.getText());
        return map;
    }


//    public static void main(String[] args) throws MyUtilLocalException, InvocationTargetException, InstantiationException, IllegalAccessException, IntrospectionException, NoSuchMethodException, NoSuchFieldException, ClassNotFoundException {
//        Map<String,Object> map = new HashMap<String, Object>();
//        map.put("id",1);
//        map.put("name","我去");
//        map.put("state",true);
//        System.out.println(mapToObject(map,a.class).toString());
//    }
}

